package com.tmt.spring.validation;

import com.tmt.annotation.parser.LazyLoadParser;
import com.tmt.helper.ReflectHelper;
import com.tmt.helper.StringHelper;
import org.springframework.util.StringUtils;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.text.MessageFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

public abstract class TmtCommonValidator<T extends Serializable> implements Validator, IRepeatable {

    public abstract String selectMethodName(T obj, Field field, String methodName);


    public List<Field> getAllRequiredFields(T object){
       return ReflectHelper.findAllFieldByAnnotation(object.getClass(),
               RequiredField.class, 0);
    }

    @Override
    public void validate(Object target, Errors errors) {

        T viewObject = (T)target;

        //查找出T类上标注了RequiredWhenRegister注解的所有字段
        List<Field> fields = getAllRequiredFields(viewObject);
        if (fields == null || fields.size() <= 0)
            return;

        fields.stream().forEach(x->{

            RequiredField annotation = x.getAnnotation(RequiredField.class);
            String label = annotation.label();
            boolean repeatable = annotation.repeatable();
            String errorCode = !StringUtils.isEmpty(annotation.errorCode()) ?
                    (annotation.errorCode().endsWith(".") ? annotation.errorCode() + x.getName() :annotation.errorCode()) :
                    MessageFormat.format("error.{0}.{1}", LazyLoadParser.getValidSimpleClassName(viewObject.getClass()), x.getName());

            //获取字段值
            Object fieldValue = ReflectHelper.getFieldValue(viewObject, x.getName());

            //首先, 判断字段值是否为空或NULL。
            if (fieldValue == null || ((fieldValue instanceof String) && StringUtils.isEmpty(fieldValue))) {
                errors.rejectValue(x.getName(), errorCode, label + "不能为空");
                return;
            }

            //然后, 根据配置的校验类进行校验。
            try {
                Class<?> validatorClass = Class.forName(annotation.validatorClass());
                if (validatorClass != null) {
                    String methodName = annotation.methodName();
                    if (methodName.contains("*"))
                        methodName = selectMethodName(viewObject, x, methodName);

                    if (StringUtils.isEmpty(methodName)) {
                        methodName = "validate" + StringHelper.upperFirst(x.getName());
                    }

                    String[] methodParam = annotation.methodParam();

                    //获取方法的参数类型数组和参数值数组
                    Class[] paramType = new Class[1 + methodParam.length];
                    Object[] paramValue = new Object[paramType.length];

                    paramType[0] = x.getType();
                    paramValue[0] = ReflectHelper.getFieldValue(viewObject, x.getName());

                    for(int i= 1; i<paramType.length; i++) {
                        Method tmpMethod = viewObject.getClass().getMethod("get" + StringHelper.upperFirst(methodParam[i-1]));
                        if (tmpMethod != null) {
                            paramType[i] = tmpMethod.getReturnType();
                            paramValue[i] = tmpMethod.invoke(viewObject);
                        }else{
                            paramType[i] = String.class;
                            paramValue[i] = methodParam[i-1];
                        }
                    }

                    Method method = validatorClass.getMethod(methodName, paramType);
                    ValidateRule vRule = method.getAnnotation(ValidateRule.class);
                    String rule = vRule != null && !StringUtils.isEmpty(vRule.value()) ? vRule.value() : "";

                    Object result = Modifier.isStatic(method.getModifiers()) ? method.invoke(null, paramValue)
                            : method.invoke(validatorClass.newInstance(), paramValue);
                    if (result instanceof Boolean && !((Boolean)result)){
                        errors.rejectValue(x.getName(), errorCode, rule);
                        return;
                    }else if (result instanceof Integer && (Integer)result != 0){
                        //errCode格式：result > 0时，等于errorCode + result，result < 0 时, 等于errorCode + "_" + result的绝对值
                        String errCode = errorCode + ((Integer)result > 0 ? result : "_" + Math.abs((Integer)result));
                        errors.rejectValue(x.getName(), errCode, rule);
                        return;
                    }
                }
            }catch (Exception ex){
            }

            //最后，检测对字段值进行重复性校验。
            if (isRepeated(target, x.getName(), fieldValue, repeatable)){
                errorCode = !StringUtils.isEmpty(errorCode) ? errorCode + ".repeated" : null;
                errors.rejectValue(x.getName(), errorCode, MessageFormat.format("{0}不可重复", label));
            }

        });

    }

    /**
     * 获取必填字段的RegFieldVo对象信息。
     * @param viewObject 要获取信息的视图对象。
     * @param regInfos 要获取信息的字段名称列表
     * @return
     */
    public final List<RegFieldDescriptor> getRegFieldDescriptors(T viewObject, List<String> regInfos){
        List<RegFieldDescriptor> regFieldVos = new ArrayList<>();

        Map<String, String> validateRule = getValidateRuleMap(viewObject);

        List<Field> fields = ReflectHelper.findAllFieldByAnnotation(viewObject.getClass(), RequiredField.class, 0);
        regInfos.stream().forEachOrdered(x->{

            Optional<Field> optional = fields.stream().filter(y-> Objects.equals(y.getName(), x)).findFirst();
            if (optional.isPresent()){
                Object fieldValue = ReflectHelper.getFieldValue(viewObject, optional.get().getName());
                RequiredField annotation = optional.get().getAnnotation(RequiredField.class);
                String key = optional.get().getName();
                boolean display = annotation.display();
                boolean displayLabel = annotation.displayLabel();
                String hint = validateRule.keySet().contains(key) ? validateRule.get(key) : "";
                hint = !StringUtils.isEmpty(annotation.fieldHint()) ? annotation.fieldHint() : hint;
                regFieldVos.add(new RegFieldDescriptor(annotation.label(), x, hint, fieldValue, display, displayLabel));
            }
        });
        return regFieldVos;
    }

    /**
     * 获取各必填字段的效验规则描述。
     * @return 字段名称——效验规则映射
     */
    public final Map<String, String> getValidateRuleMap(T viewObject){
        ConcurrentMap<String, String> map = new ConcurrentHashMap<>();

        //查找出T类上标注了ConfigurableWhenRegister注解的所有字段
        List<Field> fields = ReflectHelper.findAllFieldByAnnotation(viewObject.getClass(),
                RequiredField.class, 0);

        fields.stream().forEach(x->{
            RequiredField annotation = x.getAnnotation(RequiredField.class);
            //然后根据配置的校验类进行校验。
            try {
                Class<?> validatorClass = Class.forName("com.tmt.spring.validation.RegisterValidateHelper");
                try {
                    validatorClass = Class.forName(annotation.validatorClass());
                }catch (Exception ex){
                }

                if (validatorClass != null) {
                    String methodName = annotation.methodName();
                    if (methodName.contains("*"))
                        methodName = selectMethodName(viewObject, x, methodName);

                    //如果方法名称无效，则采用默认方法名称
                    if (StringUtils.isEmpty(methodName)) {
                        methodName = "validate" + StringHelper.upperFirst(x.getName());
                    }

                    String[] methodParam = annotation.methodParam();

                    //获取方法的参数类型数组和参数值数组
                    Class[] paramType = new Class[1 + methodParam.length];
                    paramType[0] = x.getType();
                    for(int i= 1; i<paramType.length; i++) {
                        Method tmpMethod = viewObject.getClass().getMethod("get" + StringHelper.upperFirst(methodParam[i-1]));
                        if (tmpMethod != null) {
                            paramType[i] = tmpMethod.getReturnType();
                        }
                    }

                    Method method = validatorClass.getMethod(methodName, paramType);
                    ValidateRule vRule = method.getAnnotation(ValidateRule.class);
                    String rule = vRule != null && !StringUtils.isEmpty(vRule.value()) ? vRule.value() : "";

                    map.put(x.getName(), rule);
                }
            }catch (Exception ex){
            }

        });

        return map;
    }

}
